/*
 * timer.c
 *
 *  Created on: 12/10/2010
 *      Author: wendell
 *
 *  	=== Timer ===
 *
 *  	O Programmable Interval Timer (PIT, modelo 8253 ou 8254), também
 *  chamado Relógio do Sistema (System Clock), é um chip que gera com exatidão
 *  interrupções em intervalos de tempo regulares. O chip em si possui 3 canais:
 *  	O Canal 0 está restringido para o IRQ0, para interromper o CPU em tempos
 *  regulares e previstos, Canal 1 é especifico do sistema, e Canal 2 é conectado
 *  ao speaker do sistema. Como você pode ver, este chip simples oferece vários
 *  serviços muito importantes para o sistema.
 *  	Os canais que nos interessam sãocom o Canal 0 e o 2. Você pode usar o
 *  Canal 2 para fazer o computador dar bips. Neste arquivo, nós estamos
 *  interessados somente com o Canal 0 - mapeado para o IRQ0. Este canal único
 *  de tempo permitirá a agendar novos processos mais tarde, assim como permite
 *  definir que a tarefa corrente deve esperar um certo período de tempo.
 *  	Por padrão, este canal de tempo é atribuído para gerar um IRQ0 18.222
 *  vezes por segundo. Este é o padrão da BIOS IBM PC/AT. Essa frequencia de
 *  18.222Hz foi escolhido porque se usarmos um contador de 16 bits, ele vai
 *  zerar a cada hora.
 *   	Para atribuir uma taxa personalizada em que o canal 0 do tempo dispare
 *  um IRQ0, nós devemos usar a função outportb para escrever para portas E/S.
 *  Existe um registrador de Dados para cada um dos 3 canais do temporizador em
 *  0x40, 0x41, e 0x42 respectivamente, e um registrador de Comando em 0x43.
 *  	A taxa de dados é na verdade registrador 'divisor' para este dispositivo.
 *  O temporizador vai dividir sua entrada de relógio de 1.19MHz (1193180Hz)
 *  pelo número que você preencheu no registrador de dados para obter quantas
 *  vezes por segundo ele vai disparar o sinal para aquele canal.
 *  	Você deve primeiramente selecionar o canal que nós vamos atualizar usando
 *  o registrador de comando antes escrever para o registrador dados/divisor.
 *  O que é mostrado nas duas tabelas seguintes é as definições de bit para o
 *  registrador de comando, assim como alguns modos do temporizador.
 *
 *  |7	  6 | 5   4 | 3	        1 | 0
 *   CNTR     RW      Mode	        BCD
 *
 *  CNTR - Contador # (0-2)
 *  RW - modo Leitura (Read) Escrita (Write)
 *  (1 = LSB, 2 = MSB, 3 = LSB então MSB)
 *  Modo - Veja tabela abaixo
 *  BCD - (0 = contador 16-bit, 1 = 4x BCD contadores de década)
 *
 *  Modo	Descrição
 *  0	Interrupção em conta terminal
 *  1	Retificar Hardware em um tiro
 *  2	Gerador de Taxa
 *  3	Modo Onda Quadrada
 *  4	Software Estroboscópio
 *  5	Hardware Estroboscópio
 *
 *  	As tabelas acima mostram as definições de bit para Registrador de Comando
 *  de chips 8253 e 8254 em 0x43.
 *
 * */

#include <io.h>
#include <system.h>
#include <string.h>
#include <graphic.h>

//controlador do relogio
void updateRelogio(void);

//imprime a data atual
void printTime(void);

//Guarda os tiques do relogio
int timer = 0;
int sec = 0;

datetime_t date;

static unsigned char readCMOS(unsigned char addr) {
	outport_wait(0x70,addr);
	return inport_wait(0x71);
}

datetime_t getDatetimeNow(void) {
	datetime_t now;

	cli();
	now.sec = BCD2BIN(readCMOS(CMOS_GET_SEC));
	now.min = BCD2BIN(readCMOS(CMOS_GET_MIN));
	now.hour = BCD2BIN(readCMOS(CMOS_GET_HOUR));
	now.day = BCD2BIN(readCMOS(CMOS_GET_DAY));
	now.month = BCD2BIN(readCMOS(CMOS_GET_MONTH));
	now.year = BCD2BIN(readCMOS(CMOS_GET_YEAR));
	sti();

	return now;
}

/*
 * Trata o temporizador.
 * Basta incrementar o 'timer' sempre que o temporizador dispara.
 * 		Por padrão, o temporizador dispara 18.222 vezes por segundo, ou seja,
 * uma vez a cada 0,055s
 * 		São 18.222Hz porque se usarmos um contador de 16 bits, ele vai zerar
 * a cada hora. Ainda prefiro a teoria que o engenheiro da IBM tinha fumado
 * alguma coisa, afinal, anos 80...
 * 		Isso me leva a pensar o seguinte: Entao o meu timer vai zerar em algum
 * momento... Sao 32 bits, uma hora vai dar overflow... Por enquanto nao vou
 * tratar isso, mas teoricamente seria apenas um if(timer==ultimoInt) timer=0;
 */
void timerHandler(struct regs *r) {
	//incrementa o timer
	timer++;

	date = getDatetimeNow();
	updateRelogio();

}

/**
 * Funcao que permite ficar em um laco ateh o tempo dado ser alcancado
 */
void timerWait(int ticks) {
	ulong eticks;

	eticks = timer + ticks;
	while(timer < eticks);
}

/**
 * Instala o relogio do sistema, no IRQ0 (padrao)
 */
void timerInstall() {
	//instala o timerHandler no IRQ0
	irqInstallHandler(0, timerHandler);
}

/**
 * Relogio do sistema
 */
void updateRelogio() {
	int x = getX();
	int y = getY();
	//Toda 18 vez (aproximadamente 1 seg), mostra o relogio
	if(timer % 18 == 0) {
		setTextColor(0, 15);
		gotoXY(69, 0);

		putchar(' ');
		printTime();
		putchar(' ');

		restoreColor();
		gotoXY(x, y);
	}
}

// Imprime a hora do sistema
void printTime() {

	char sec[3] = " ";
	char min[3] = " ";
	char h[3] = " ";

	itoa(date.hour-3, h);
	if(date.hour-3 < 10)
		putchar('0');
	print(h); putchar(':');

	itoa(date.min, min);
	if(date.min < 10)
		putchar('0');
	print(min); putchar(':');

	itoa(date.sec, sec);
	if(date.sec<10)
		putchar('0');
	print(sec);
}

// Imprime a data do sistema
void printDate() {

	char day[3] = " ";
	char month[3] = " ";
	char year[5] = " ";
	itoa(date.day, day);
	itoa(date.month, month);
	itoa(date.year, year);
	print(day);
	putchar('/');
	print(month);
	putchar('/');
	print(year);

}





